<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Document</title>
</head>

<body>
  <script>
    //#region call
    Function.prototype.call2 = function (ctx) {
      console.log(this); //ƒ bar() {    console.log(this.name);   }
      // this(); //不执行
      ctx.fn = this; //this指向的是使用call方法的函数(Function的实例，即下面测试例子中的bar方法)
      // 浏览器中是window，其它环境（如node）则是global
      if (!ctx) {
        //context为null或者是undefined
        ctx = typeof window === "undefined" ? global : window;
      }
      let rest = [...arguments].slice(1); //用slice方法取第二个到最后一个参数（获取除了this指向对象以外的参数）, 空数组slice后返回的仍然是空数组

      let result = ctx.fn(...rest); //fn里的this指向调用他的对象ctx ctx --> obj

      delete ctx.fn;

      return result; //返回结果
    };

    let obj = {
      name: "龟Ⅴ",
    };

    function bar(name, what, who) {
      console.log(this.name);
      console.log(`${name} ${what} ${who}`);
    }

    bar.call2(obj, "tc", "is", "嘛喽");
    //#endregion

    //#region apply

    // 第二个参数是数组或类数组
    Function.prototype.apply = function (context, rest) {
      if (!context) {
        //context为null或者是undefined时,设置默认值
        context = typeof window === "undefined" ? global : window;
      }
      context.fn = this;
      let result;
      // 是不是数组参数
      if (rest === undefined || rest === null) {
        //undefined 或者 是 null 不是 Iterator 对象，不能被 ...
        result = context.fn(rest);
      } else if (typeof rest === "object") {
        result = context.fn(...rest);
      }
      delete context.fn;
      return result;
    };

    // 测试代码
    var foo = {
      name: "Selina",
    };
    var name = "Chirs";
    function bar(job, age) {
      console.log(this.name);
      console.log(job, age);
    }
    bar.apply(foo, ["programmer", 20]);
    // Selina programmer 20
    bar.apply(null, ["teacher", 25]);
    //#endregion

    //#region bind
    Function.prototype.myBind = function (context, ...args) {
      // 1. 检查调用者是否为函数
      if (typeof this !== 'function') {
        throw new TypeError('Bind must be called on a function');
      }

      // 2. 保存原函数（this 指向调用 bind 的函数）
      const originalFunc = this;

      // 3. 返回一个新函数，该函数可以接收剩余参数
      const boundFunc = function (...innerArgs) {
        // 4. 判断是否通过 new 调用（如果是 new，this 指向新对象，否则指向 context）
        const isNewCall = this instanceof boundFunc;
        const thisArg = isNewCall ? this : context;

        // 5. 调用原函数，并绑定 this 和合并参数
        return originalFunc.apply(thisArg, [...args, ...innerArgs]);
      };

      // 6. 维护原型链（使 new 操作符能正确继承原函数的原型）
      if (originalFunc.prototype) {
        boundFunc.prototype = Object.create(originalFunc.prototype);
      }

      return boundFunc;
    };
    //#endregion
  </script>
</body>

</html>